package src.model.gameWorld;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import src.view.ViewEventHandler;
import src.model.gameObject.GameObject;
import src.event.events.MapUpdateEvent;

/**
 * <b>Overview<b><br>
 * <p>The PlayerMap represnts the game world as the current (owning) player
 * currently believes it appears.  It maintains the notion of fog of war, and
 * updates itself based on the ActualMap</p>
 * <br><br>
 * <b>Responsibilities</b><br>
 * <p>The PlayerMap is responsbile for represnting the world as the player thinks
 * it exists.</p>
 * <br><br>
 * <b>Collaborators</b><br>
 * <ul>
 * <li>GameDataObject - Subcontractor: GameDataObjects are used by the PlayerMap
 * to represent the concept of a visible GameObject</li>
 * <li>Position - Subcontractor: the Position class is used by the Actual 
 * map to interact with the concept of positions</li>
 * </ul>
 * <br><br>
 * <b>Implementor</b> - Brent <br>
 * <b>Testor</b> - Tim <br>
 * @author Brent
 *
 */
public class PlayerMap 
{
	
	public enum TileStatus { Unseen, seen, seeing };
	private class PlayerMapTile
	{
		public TileStatus status;
		public int viewingObjects;
	}
	
	private PlayerMapTile[][] myView;
	private Map<Position, ArrayList<GameDataObject>> myPositionMapping; 
	private ActualMap myMap;	
	
	/**
	 * Constructs a new PlayerMap that will represent a certain player's view
	 * of the provided ActualMap.
	 * 
	 * @param actualMap - the ActualMap to be represented by this player map
	 * @param player - the Player that this PlayerMap is tied to
	 */
	public PlayerMap(ActualMap actualMap)
	{
		myView = new PlayerMapTile[Position.getXBoundary()][Position.getYBoundary()];
		
		for(int row = 0; row < myView.length; row++)
			for(int col = 0; col < myView[0].length; col++)
			{
				myView[row][col] = new PlayerMapTile();
				myView[row][col].status = TileStatus.Unseen;
				myView[row][col].viewingObjects = 0;
			}
		
		myPositionMapping = new HashMap<Position, ArrayList<GameDataObject>>();
		
		myMap = actualMap;

//		ViewEventHandler.fireEvent(new MapUpdateEvent(this));
	}
	
	/**
	 * Expands the Viewable area represented within this Playermap
	 * 
	 * @param p - the Position at which the new viewable space orginates
	 * @param radius - the radius of viewable area
	 */
	public void ExpandVision(Position p, int radius)
	{
		for(int row = 0; row < myView.length; row++)
			for(int col = 0; col < myView[0].length; col++)
			{
				if(myMap.distanceBetween(new Position(col,row), p) <= radius)
					myView[row][col].viewingObjects++;
				if(myView[row][col].viewingObjects > 0) 
					myView[row][col].status = TileStatus.seeing;
			}

		ViewEventHandler.fireEvent(new MapUpdateEvent(this));
	}
	
	/**
	 * Reduces the Viewable area represented within this Playermap
	 * 
	 * @param p - the Position at which the current viewable space orginates
	 * @param radius - the radius of viewable area
	 */
	public void ReduceVision(Position p, int radius)
	{
		for(int row = 0; row < myView.length; row++)
			for(int col = 0; col < myView[0].length; col++)
			{
				if(myMap.distanceBetween(new Position(col,row), p) <= radius)
				{
					if(myView[row][col].viewingObjects > 0)
						myView[row][col].viewingObjects--;
				
					if(myView[row][col].viewingObjects == 0)
						myView[row][col].status = TileStatus.seen;
				}
			}
		ViewEventHandler.fireEvent(new MapUpdateEvent(this));
	}
	
	/**
	 * Updates the PlayerMap based on the current Viewable Areas, the actual
	 * map, and the current player
	 *
	 */
	
	public TileStatus getTileStatus(Position p){
		return myView[p.X()][p.Y()].status;
	}

	public void UpdateMap()
	{
		for(int row = 0; row < myView.length; row++)
			for(int col = 0; col < myView[0].length; col++)
				if(myView[row][col].status == TileStatus.seeing)
				{
					ArrayList<GameDataObject> GDOs =  new ArrayList<GameDataObject>();
					ArrayList<GameObject> GOs = myMap.findObject(new Position(col, row));

					if(GOs != null){
						for(GameObject go : GOs){
							GameDataObject gdo = new GameDataObject(go);
							GDOs.add(gdo);
						}
					}	
					myPositionMapping.put(new Position(col, row), GDOs);
				}
		
		ViewEventHandler.fireEvent(new MapUpdateEvent(this));
	}
	
	/**
	 * Returns an ArrayList contain all GameDataObjects at a certain position
	 * 
	 * @param p - the position being queried
	 * @return a java.util.ArrayList containg all GameDataObjects at position p
	 */
	public ArrayList<GameDataObject> GetObjectsAtPosition(Position p)
	{
		return myPositionMapping.get(p);
	}
	
	/**
	 * Returns a mapping of all of the currently viewable positions to the 
	 * GameDataObject(s) that are on them
	 *  
	 * @return a java.util.HashMap of all currently viewable positions and
	 * their GameDataObjects
	 */
	public Map<Position, ArrayList<GameDataObject>> getAllGameDataObjects()
	{
		return myPositionMapping;
	}
	
	public static void main(String[] args)
	{
		ActualMap am = new ActualMap(10, 10, MapTileConfiguration.Hexagonal);
		PlayerMap pm = new PlayerMap(am);
		
		for(int row = 0; row < pm.myView.length; row++)
		{
			for(int col = 0; col < pm.myView[0].length; col++)
			{
				System.out.print(pm.myView[row][col].viewingObjects + " ");
			}
			System.out.println();
		}
		System.out.println();

		pm.ExpandVision(new Position(4,4), 3);
		
		for(int row = 0; row < pm.myView.length; row++)
		{
			for(int col = 0; col < pm.myView[0].length; col++)
			{
				System.out.print(pm.myView[row][col].viewingObjects + " ");
			}
			System.out.println();
		}
		System.out.println();
		
		pm.ExpandVision(new Position(2,2), 3);
		
		for(int row = 0; row < pm.myView.length; row++)
		{
			for(int col = 0; col < pm.myView[0].length; col++)
			{
				System.out.print(pm.myView[row][col].viewingObjects + " ");
			}
			System.out.println();
		}
		System.out.println();
		
		pm.ReduceVision(new Position(4,4), 3);
		
		for(int row = 0; row < pm.myView.length; row++)
		{
			for(int col = 0; col < pm.myView[0].length; col++)
			{
				System.out.print(pm.myView[row][col].viewingObjects + " ");
			}
			System.out.println();
		}
		System.out.println();
		
	}
}
